/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


class XTexture {
    static ximages = [];
    static all_cuts = {};
    static tmp_cutid = 0;
    static ai_cutid = 100;
    static text_imgs = {};//rid,{mask}
    static text_idxs = {};//text,{}
    static needInit_ = true;
    static USE_DPR = 1;
    static Init(dpr) {
        if (XTexture.needInit_) {
            XTexture.text_cvs = document.createElement('canvas');
            XTexture.image_cvs = document.createElement('canvas');
            XTexture.needInit_ = false;
        }
        XTexture.USE_DPR = dpr;
        XTexture.text_cvs.width = Math.round(1024 * dpr);
        XTexture.text_cvs.height = Math.round(256 * dpr);
        XTexture.text_cvs.style.width = 1024 + 'px';
        XTexture.text_cvs.style.height = 256 + 'px';
        XTexture.text_ctx = XTexture.text_cvs.getContext("2d");
        XTexture.text_ctx.textBaseline = 'top'
        XTexture.text_ctx.textAlign = 'left'
        XTexture.text_ctx.fillStyle = "rgba(255,255,255,1)";
        XTexture.text_ctx.scale(dpr, dpr);

        XTexture.image_cvs.width = Math.round(1024 * dpr);
        XTexture.image_cvs.height = Math.round(1024 * dpr);
        XTexture.image_cvs.style.width = 1024 + 'px';
        XTexture.image_cvs.style.height = 1024 + 'px';
        XTexture.image_ctx = XTexture.image_cvs.getContext("2d");
        XTexture.image_ctx.fillStyle = "rgba(255,255,255,1)";
        XTexture.image_ctx.scale(1, 1);
        // XTexture.SYSTEM_FONT = (document.documentElement.currentStyle ? document.documentElement.currentStyle : window.getComputedStyle(document.documentElement)).fontFamily;
    }
    static _init_texture_status(tex) {
        // window.gl.pixelStorei(window.gl.UNPACK_FLIP_Y_WEBGL, true);
        window.gl.activeTexture(window.gl.TEXTURE0);
        window.gl.bindTexture(window.gl.TEXTURE_2D, tex);
        window.gl.pixelStorei(window.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
        window.gl.texParameteri(window.gl.TEXTURE_2D, window.gl.TEXTURE_MIN_FILTER, window.gl.NEAREST);//  LINEAR  插值           模糊 抗锯齿
        window.gl.texParameteri(window.gl.TEXTURE_2D, window.gl.TEXTURE_MAG_FILTER, window.gl.NEAREST);//  NEAREST 使用最近像素值 清晰 有锯齿
        window.gl.texParameteri(window.gl.TEXTURE_2D, window.gl.TEXTURE_WRAP_S, window.gl.CLAMP_TO_EDGE);
        window.gl.texParameteri(window.gl.TEXTURE_2D, window.gl.TEXTURE_WRAP_T, window.gl.CLAMP_TO_EDGE);
    }
    static Loaded(path) {
        for (let i = 0; i < XTexture.ximages.length; i++) {
            if (i in XTexture.ximages && XTexture.ximages[i]["path"] == path) {
                return XTexture.ximages[i];
            }
        }
        return false;
    }
    static Rid(path) {
        for (let i = 0; i < XTexture.ximages.length; i++) {
            if (i in XTexture.ximages && XTexture.ximages[i]["path"] == path) {
                return i;
            }
        }
        return -1;
    }
    static createTexture(w, h, bpp = 3) {
        var rid = XTexture.ximages.length;

        var texture = window.gl.createTexture();
        XTexture._init_texture_status(texture);

        if (bpp == 3) {
            window.gl.texImage2D(window.gl.TEXTURE_2D, 0, window.gl.RGB, w, h, 0, window.gl.RGB, window.gl.UNSIGNED_BYTE, null);
        } else if (bpp == 4) {
            window.gl.texImage2D(window.gl.TEXTURE_2D, 0, window.gl.RGBA, w, h, 0, window.gl.RGBA, window.gl.UNSIGNED_BYTE, null);
        }

        XTexture.ximages[rid] = { "stat": 1, "path": "custom", "tex": texture, "w": w, "h": h };
        return rid
    }
    static LoadTextureFromImage2(image) {
        var rid = XTexture.ximages.length;

        var texture = window.gl.createTexture();
        XTexture._init_texture_status(texture);

        // let tmp = new Uint8Array([255, 255, 255, 255])
        window.gl.texImage2D(window.gl.TEXTURE_2D, 0, window.gl.RGBA, window.gl.RGBA, window.gl.UNSIGNED_BYTE, image)

        XTexture.ximages[rid] = { "stat": 1, "path": "image", "tex": texture, "w": image.width, "h": image.height };
        return rid
    }
    static LoadTextureFromImage(path, keepdata = false) {
        if (path == "CUSTOM_TEXTURE_1") {
            var rid = XTexture.ximages.length;

            var texture = window.gl.createTexture();
            XTexture._init_texture_status(texture);

            let tmp = new Uint8Array([255, 255, 255, 255])
            window.gl.texImage2D(window.gl.TEXTURE_2D, 0, window.gl.RGBA, 1, 1, 0, window.gl.RGBA, window.gl.UNSIGNED_BYTE, tmp)

            XTexture.ximages[rid] = { "stat": 1, "path": path, "tex": texture, "w": 1, "h": 1 };
            return rid
        }
        else {
            for (let i = 0; i < XTexture.ximages.length; i++) {
                if (i in XTexture.ximages && XTexture.ximages[i]["path"] == path) {
                    return i;
                }
            }
            var rid = XTexture.ximages.length;
            XTexture.ximages[rid] = { stat: 0, path: path, tex: null };
            var image = new Image();
            image.src = path;//"http://localhost:8910/"+
            image.onload = function () {
                var texture = window.gl.createTexture();
                XTexture._init_texture_status(texture)

                window.gl.texImage2D(window.gl.TEXTURE_2D, 0, window.gl.RGBA, window.gl.RGBA, window.gl.UNSIGNED_BYTE, image);

                XTexture.ximages[rid].tex = texture;
                XTexture.ximages[rid].img = image;
                XTexture.ximages[rid].stat = 1;
                XTexture.ximages[rid].w = image.width;
                XTexture.ximages[rid].h = image.height;
                XTexture.ximages[rid].cid = XTexture.MakeCut(rid);

                if (keepdata) {
                    XTexture.image_ctx.clearRect(0, 0, 1024, 1024);
                    XTexture.image_ctx.drawImage(image, 0, 0, image.width, image.height);
                    let data = XTexture.image_ctx.getImageData(0, 0, image.width, image.height).data;
                    let size = image.width * image.height;
                    let out = new Uint32Array(size);
                    for (let i = 0; i < size; i++) {
                        out[i] = (data[i * 4] << 24) | (data[i * 4 + 1] << 16) | (data[i * 4 + 2] << 8) | data[i * 4 + 3];
                    }
                    XTexture.ximages[rid].data = out;
                    // console.log(data);
                }
            };
            return rid;
        }
    }
    static GetImage(rid) {
        // if (XTexture.ximages[rid].stat != 1) return null;
        return XTexture.ximages[rid];
    }
    static GetCut(cid) {
        return XTexture.all_cuts[cid];
    }
    static TmpCut(rid, x = 0, y = 0, w = -1, h = -1) {
        if (XTexture.ximages[rid].stat != 1) return -1;

        let ww = XTexture.ximages[rid].w;
        let hh = XTexture.ximages[rid].h;

        if (w == -1) w = ww;
        if (h == -1) h = hh;
        XTexture.all_cuts[XTexture.tmp_cutid] = {
            "rid": rid,
            "x": x, "y": y,
            "w": w, "h": h,
            "ww": ww, "hh": hh,
            "sw": 1, "sh": 1,
            "u0": x / ww, "v0": y / hh,
            "u1": (x + w) / ww, "v1": y / hh,
            "u2": (x + w) / ww, "v2": (y + h) / hh,
            "u3": x / ww, "v3": (y + h) / hh
        };
        XTexture.tmp_cutid += 1;
        return XTexture.tmp_cutid - 1;
    }
    static MakeCut(rid, x = 0, y = 0, w = -1, h = -1, ww = -1, hh = -1, sw = 1, sh = 1) {
        if (XTexture.ximages[rid].stat != 1) return -1;

        if (ww == -1) ww = XTexture.ximages[rid].w;
        if (hh == -1) hh = XTexture.ximages[rid].h;
        if (w == -1) w = ww;
        if (h == -1) h = hh;
        let cid = XTexture.ai_cutid++;
        XTexture.all_cuts[cid] = {
            "rid": rid,
            "x": x, "y": y,
            "w": w, "h": h,
            "ww": ww, "hh": hh,
            "sw": sw, "sh": sh,
            "u0": x / ww, "v0": y / hh,
            "u1": (x + w) / ww, "v1": y / hh,
            "u2": (x + w) / ww, "v2": (y + h) / hh,
            "u3": x / ww, "v3": (y + h) / hh
        };
        return cid;
    }
    static AdjustCut(cid, x = 0, y = 0, w = -1, h = -1, ww = -1, hh = -1) {
        let rid = XTexture.all_cuts[cid].rid;
        if (ww == -1) ww = XTexture.ximages[rid].w;
        if (hh == -1) hh = XTexture.ximages[rid].h;
        if (w == -1) w = ww;
        if (h == -1) h = hh;
        XTexture.all_cuts[cid] = {
            "rid": rid,
            "x": x, "y": y,
            "w": w, "h": h,
            "ww": ww, "hh": hh,
            "sw": 1, "sh": 1,
            "u0": x / ww, "v0": y / hh,
            "u1": (x + w) / ww, "v1": y / hh,
            "u2": (x + w) / ww, "v2": (y + h) / hh,
            "u3": x / ww, "v3": (y + h) / hh
        };
    }

    static PutTexture(tex, w, h) {
        var rid = XTexture.ximages.length;
        XTexture.ximages[rid] = { "stat": 1, "path": "put" + rid, "tex": tex, "w": w, "h": h };
        return rid;
    }
    static PutImage(image) {
        var texture = window.gl.createTexture();
        XTexture._init_texture_status(texture)

        window.gl.texImage2D(window.gl.TEXTURE_2D, 0, window.gl.RGBA, window.gl.RGBA, window.gl.UNSIGNED_BYTE, image);

        return XTexture.PutTexture(texture, image.width, image.height);
    }

    static _LoadTexture(width, height) {
        var rid = XTexture.ximages.length;

        var texture = window.gl.createTexture();
        XTexture._init_texture_status(texture)
        window.gl.texImage2D(window.gl.TEXTURE_2D, 0, window.gl.RGBA, width, height, 0, window.gl.RGBA, window.gl.UNSIGNED_BYTE, null)

        XTexture.ximages[rid] = { "stat": 1, "path": "default" + rid, "tex": texture, "w": width, "h": height };
        return rid;
    }
    static splitText(ss, size, w) {
        XTexture.text_ctx.font = size + "px " + XFont.FONT_SUFFIX;
        let ret = [];
        let line = "";
        for (let s of ss) {
            if (s == "\n") {
                ret.push(line);
                line = "";
            } else if (XTexture.text_ctx.measureText(line + s).width > w) {
                ret.push(line);
                line = s;
            } else {
                line += s;
            }
        }
        if (line.length > 0) {
            ret.push(line);
        }
        return ret;
    }
    static _InitTextImageData(s, size) {
        XTexture.text_ctx.clearRect(0, 0, 1024, 256);
        // XTexture.text_ctx.font = size + "px 微软雅黑";//宋体,黑体,微软雅黑,微软正黑体,楷体,新宋体,仿宋
        XTexture.text_ctx.font = size + "px " + XFont.FONT_SUFFIX;
        // XTexture.text_ctx.font = size + "px";
        // XTexture.text_ctx.font = size + "px " + XTexture.SYSTEM_FONT;
        // XTexture.text_ctx.fillText(s, 12, 12, 1000);
        // XTexture.text_ctx.fillStyle = "rgba(0,0,255,1)";
        // XTexture.text_ctx.fillRect(0, 0, 1024, 256);
        // XTexture.text_ctx.fillStyle = "rgba(255,255,255,1)";
        XTexture.text_ctx.fillText(s, 0, 12, 1000);
        {
            // actualBoundingBoxAscent:0.75
            // actualBoundingBoxDescent:20.25
            // actualBoundingBoxLeft:-1
            // actualBoundingBoxRight:76.189453125
            // fontBoundingBoxAscent:5.75
            // fontBoundingBoxDescent:20.25
            // width:76.982421875
            let wh = XTexture.text_ctx.measureText(s);
            // let x1 = parseInt((12 + wh.actualBoundingBoxLeft - 0.99) * XTexture.USE_DPR);
            // let x2 = parseInt((12 + wh.actualBoundingBoxRight + 0.99) * XTexture.USE_DPR);
            // let y1 = parseInt((12 + -wh.actualBoundingBoxAscent - 0.99) * XTexture.USE_DPR);
            // let y2 = parseInt((12 + wh.actualBoundingBoxDescent + 0.99 + 0.5) * XTexture.USE_DPR);
            let x1 = 0;
            let x2 = wh.width * XTexture.USE_DPR;
            let y1 = (12 - wh.fontBoundingBoxAscent) * XTexture.USE_DPR;
            let y2 = (12 + wh.fontBoundingBoxDescent) * XTexture.USE_DPR;

            let sw = 1;
            if (x2 > 1024) {
                sw = (x2 - x1) / (1024 - x1);
                x2 = 1024;
            }

            if (1 == 0) {
                let imgd = XTexture.text_ctx.getImageData(0, 0, 1024, 256).data
                let rx1 = 0;
                let ry1 = 0;
                let rx2 = 100;
                let ry2 = 100;
                for (let x = 0; x < 1024; x++) {
                    for (let y = 0; y < 256; y++) {
                        let p = (y * 1024 + x) * 4;
                        if (imgd[p] != 0) {
                            rx1 = x;
                            x = 9999;
                            break;
                        }
                    }
                }
                for (let x = 1024; x > 0; x--) {
                    for (let y = 0; y < 256; y++) {
                        let p = (y * 1024 + x) * 4;
                        if (imgd[p] != 0) {
                            rx2 = x;
                            x = -1;
                            break;
                        }
                    }
                }
                for (let y = 0; y < 256; y++) {
                    for (let x = 0; x < 1024; x++) {
                        let p = (y * 1024 + x) * 4;
                        if (imgd[p] != 0) {
                            ry1 = y;
                            y = 9999;
                            break;
                        }
                    }
                }
                for (let y = 256; y > 0; y--) {
                    for (let x = 0; x < 1024; x++) {
                        let p = (y * 1024 + x) * 4;
                        if (imgd[p] != 0) {
                            ry2 = y;
                            y = -1;
                            break;
                        }
                    }
                }
                let comp = [[rx1, ry1, rx2, ry2],
                [x1, y1, x2, y2]];
                console.log(comp);
                // [x1, y1, x2, y2] = [rx1, ry1, rx2, ry2];
            }
            try {
                return [XTexture.text_ctx.getImageData(x1, y1, x2 - x1, y2 - y1), sw];
            }
            catch (e) {
                console.log("[", s, "]");
                console.log(e);
            }
        }

        let imgd = XTexture.text_ctx.getImageData(0, 0, 1024, 256).data
        let w = 1024;
        let h = size + 5;
        let x = 128;
        while (x == 128) {
            h -= 1;
            for (x = 0; x < 128; x++) {
                let p = (h * 1024 + x) * 4;
                if (imgd[p] != 0) break;
            }
        }
        let y = h
        while (y == h) {
            w -= 1
            for (y = 0; y < h; y++) {
                let p = (y * 1024 + w) * 4;
                if (imgd[p] != 0) break;
            }
        }
        // let realw=16
        // let realh=16
        // while(realw<w+1)realw*=2
        // while(realh<h+1)realh*=2
        // return XTexture.text_ctx.getImageData(0, 0, realw, realh)
        return XTexture.text_ctx.getImageData(0, 0, w + 2, h + 2);
    }
    static GetText(s, size) {
        if (size in XTexture.text_idxs) {
            if (s in XTexture.text_idxs[size]) {
                XTexture.text_idxs[size][s].time = XTools.FRAME_SECOND;
                return XTexture.text_idxs[size][s].cid;
            }
        }
        else {
            XTexture.text_idxs[size] = {};
        }

        let [imgd, sw] = XTexture._InitTextImageData(s, size);
        let w = imgd.width;
        let h = imgd.height;

        let use_height = Math.floor((h + 31) / 32)
        let mask = 0
        for (let i = 0; i < use_height; i++)
            mask |= (1 << i);

        let rid = -1;
        let off = -1;
        for (let k in XTexture.text_imgs) {
            for (let i = 0; i < 32 - use_height + 1; i++) {
                if ((XTexture.text_imgs[k].mask & (mask << i)) == 0) {
                    off = i;
                    break;
                }
            }
            if (off != -1) {
                rid = k;
                break;
            }
        }
        if (rid == -1) {
            rid = XTexture._LoadTexture(1024, 1024);
            XTexture.text_imgs[rid] = { "mask": 0 };
            off = 0
        }

        // console.log("GetText",s, size, h, rid, off, use_height)

        let cid = XTexture.MakeCut(rid, 0, off * 32, w, h, 1024, 1024, sw, 1);
        XTexture.text_imgs[rid]["mask"] |= mask << off
        XTexture.text_idxs[size][s] = { "cid": cid, "rid": rid, "mask": mask << off, "time": XTools.FRAME_SECOND }

        let td = new Uint8Array(imgd.data);//Uint8ClampedArray
        // let td=new Uint8Array(imgd.width*imgd.height*4)
        // for(let x=0;x<imgd.width;x++)
        // {
        //     for(let y=0;y<imgd.height;y++)
        //     {
        //         let p=(y*imgd.width+x)*4
        //         imgd.data[p]=255
        //         imgd.data[p+1]=255
        //         imgd.data[p+2]=255
        //         imgd.data[p+3]=255

        //         td[p]=255
        //         td[p+1]=255
        //         td[p+2]=255
        //         td[p+3]=255

        //         // // console.log(x,y,imgd.data[p],imgd.data[p+1],imgd.data[p+2],imgd.data[p+3])
        //         // if(imgd.data[p+3]==0)continue
        //         // imgd.data[p]=0
        //         // imgd.data[p+1]=0
        //         // imgd.data[p+2]=imgd.data[p+3]
        //         // //imgd.data[p+3]=255
        //     }
        // }
        window.gl.activeTexture(window.gl.TEXTURE0);
        window.gl.bindTexture(window.gl.TEXTURE_2D, XTexture.ximages[rid].tex);
        window.gl.pixelStorei(window.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);

        window.gl.texSubImage2D(window.gl.TEXTURE_2D, 0, 0, off * 32, imgd.width, imgd.height, window.gl.RGBA, window.gl.UNSIGNED_BYTE, td);
        // window.gl.texSubImage2D(window.gl.TEXTURE_2D, 0, 0, off * 32, window.gl.RGBA, window.gl.UNSIGNED_BYTE, imgd.data);

        return cid
    }
    static _FreshText() {
        XTexture.tmp_cutid = 0;
        let rm = []
        for (let size in XTexture.text_idxs) {
            for (let s in XTexture.text_idxs[size]) {

                if (XTools.FRAME_SECOND - XTexture.text_idxs[size][s].time > 10) {
                    XTexture.text_imgs[XTexture.text_idxs[size][s].rid].mask &= (~XTexture.text_idxs[size][s].mask);
                    delete XTexture.all_cuts[XTexture.text_idxs[size][s].cid];
                    rm.push([size, s]);
                }
            }
        }
        for (let idx of rm) {
            delete XTexture.text_idxs[idx[0]][idx[1]];
        }
        //console.log("free",rm.length,Object.keys(XTexture.all_cuts).length,Object.keys(XTexture.text_imgs).length,XTexture.ai_cutid)
    }
    static ExpandColor(c) {
        return [
            ((c >> 16) & 0xff) / 255,
            ((c >> 8) & 0xff) / 255,
            (c & 0xff) / 255,
            ((c >> 24) & 0xff) / 255];//r,g,b,a
    }
}

module.exports = {
    XTexture
}

const { XTools } = require("../XTools.js");
const { XFont } = require("./XFont.js");
